/*
 * =====================================================================================
 *
 *       Filename:  cl_epoll_poller.cxx
 *
 *    Description:  
 *
 *        Version:  1.0
 *        Created:  03/30/2011 05:04:25 PM
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  Chen Ken (Ken), zlchen.ken@gmail.com
 *        Company:  EMC
 *
 * =====================================================================================
 */

#include <unistd.h>
#include <errno.h>
#include <cassert>
#include "cl_epoll_poller.hxx"

namespace neo
{
using namespace std;

CLEpollPoller::CLEpollPoller() 
	: m_readyEvents(CL_EPOLL_INIT_SIZE)
{
  if((m_epollfd = ::epoll_create1(EPOLL_CLOEXEC)) < 0)
	{
		// KENTODO..Log
	}
}

CLEpollPoller::~CLEpollPoller() 
{
	if(m_epollfd >= 0)
	{
    ::close(m_epollfd);
	}
}

int CLEpollPoller::fd(void) const
{
	return m_epollfd;
}

bool CLEpollPoller::doPoll(long millisecs, CLChannelList& chs) 
{
	assert(m_epollfd >= 0);
	if(m_channels.empty())
	{
		return false;
	}

	int readyEvents = 0;
  if((readyEvents = ::epoll_wait(m_epollfd, &m_readyEvents[0], m_readyEvents.size(), millisecs)) < 0) 
	{
		return false;
	}

  if(readyEvents > 0)
	{
		fillReadyChannels(readyEvents, chs);
		if(static_cast<size_t>(readyEvents) == m_readyEvents.size())
		{
		  m_readyEvents.resize(readyEvents*2);
		}
		return true;
	}
	else if(readyEvents == 0)
	{
	  return true;
	}
	else if(errno == EINTR)
	{
    // timeout, treat as success
		return true;
	}
	else
	{
    return false;
	}
}

bool CLEpollPoller::doAdd(CLChannel* ch) 
{
  assert((m_epollfd >= 0) && (ch != NULL));
  ChannelMap::const_iterator itr = m_channels.find(ch->fd());
	if(itr != m_channels.end())
	{
    // CLLogger
	  return false;
	}

  if(!updateEpollfd(ch, EPOLL_CTL_ADD))
	{
		return false;
	}
  
  m_channels.insert(make_pair(ch->fd(), ch));
	return true;
}

bool CLEpollPoller::doRemove(CLChannel* ch) 
{
  assert(m_epollfd >= 0);
  ChannelMap::iterator itr = m_channels.find(ch->fd());
	if(itr == m_channels.end())
	{
	  return false;
	}

	if(!updateEpollfd(ch, EPOLL_CTL_DEL))
	{
		return false;
	}

  m_channels.erase(itr);
	return true;
}

bool CLEpollPoller::doModify(CLChannel* ch) 
{
  assert((m_epollfd >= 0) && (ch != NULL));
  ChannelMap::const_iterator itr = m_channels.find(ch->fd());
	if(itr == m_channels.end())
	{
	  return false;
	}

	return updateEpollfd(ch, EPOLL_CTL_MOD);
}

bool CLEpollPoller::updateEpollfd(CLChannel* ch, int op)
{
  struct epoll_event ee;
  ee.events = ch->events();
  ee.data.ptr = ch;

  if(::epoll_ctl(m_epollfd, op, ch->fd(), &ee) < 0)
	{
		// KENTODO...Log
		return false;
	}
	
	return true;
}

void CLEpollPoller::fillReadyChannels(int readyEvents, CLChannelList& chs)
{
	chs.reserve(readyEvents);
  for(int i = 0; i < readyEvents; ++i)
	{
    CLChannel* ch = static_cast<CLChannel*>(m_readyEvents[i].data.ptr);
		ch->setReadyEvents(m_readyEvents[i].events);
    chs.push_back(ch);
	}
}

} // neo
